package org.delphy.indy2.containers
{
	import com.degrafa.GeometryGroup;
	import com.degrafa.Surface;
	import com.degrafa.geometry.splines.BezierSpline;
	import com.degrafa.paint.LinearGradientStroke;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.geom.Point;
	
	import mx.containers.Canvas;
	import mx.core.UIComponent;
	import mx.effects.WipeDown;
	import mx.effects.WipeUp;
	import mx.events.FlexMouseEvent;
	import mx.managers.PopUpManager;
	import mx.styles.StyleManager;
	
	import org.delphy.indy2.core.ICarbonComponent;
	import org.delphy.indy2.layers.*;
	import org.delphy.indy2.utils.MathUtils;

	//--------------------------------------
	//  Styles
	//--------------------------------------
	
	/**
	 * Trunk stroke weight
	 * 
	 * @default 1.3
	 */
	[Style(name="trunkWeight", type="Number", inherit="no")]

	/**
	 * Trunk Colors
	 * 
	 * @default #000000, #000000
	 */
	[Style(name="trunkColors", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Trunk Alphas
	 */
	[Style(name="trunkAlphas", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Trunk Ratios
	 */
	[Style(name="trunkRatios", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Trunk RatioUnits
	 */
	[Style(name="trunkRatioUnits", type="Array", arrayType="String", inherit="no")]
	
	public class CarbonFunTree extends Canvas implements ICarbonComponent
	{

		//--------------------------------------
		//  Tree Growth Parameters
		//--------------------------------------
		
		/**
		 * @public
		 * 
		 * @defaut 70
		 */
		public var maxBranchSpreadX:Number=70;
		
		/**
		 * @public
		 * 
		 * @default 5
		 */
		public var maxBranchSpreadY:Number=5;
		
		/**
		 * @public
		 * 
		 * @default 3
		 */
		public var minBranchSteps:Number=3;
		
		/**
		 * @public
		 * 
		 * @default 6
		 */
		public var maxBranchSteps:Number=6;
		
		/**
		 * @public
		 * 
		 * @default 10
		 */
		public var maxKnotSpreadX:Number=10;
		
		/**
		 * @public
		 */
		public static const UP_DIRECTION:String="up";
		
		/**
		 * @public
		 */
		public static const DOWN_DIRECTION:String="down";
		
		/**
		 * @public
		 * 
		 * @default UP_DIRECTION
		 */
		public var direction:String="up";

		//--------------------------------------
		//  Animation and Host
		//--------------------------------------
		
		/**
		 * @public
		 * 
		 * @default 1.2
		 */
		protected var _transitionTime:Number=1.2;
		
		public function get transitionTime():Number{
			return _transitionTime;
		}
		
		public function set transitionTime(value:Number):void{
			if(_transitionTime != value){
				_transitionTime = value;
				
				wipeUp.duration=transitionTime*1000;
				wipeDown.duration=transitionTime*1000;
			}
		}
		
		/**
		 * @protected
		 */
		protected var wipeDown:WipeDown;
		
		/**
		 * @protected
		 */
		protected var wipeUp:WipeUp;
		
		/**
		 * @protected
		 */
		protected var _host:DisplayObject=null;

		public function get host():DisplayObject
		{ 
			return _host; 
		}
		
		public function set host(value:DisplayObject):void
		{
			if (value !== _host)
			{
				_host = value;
			}
		}

		//--------------------------------------
		//  Geometry and shading
		//--------------------------------------
		
		/**
		 * @public
		 */
		protected var _positions:Array;
		
		public function get positions():Array{
			return _positions;
		}

		/**
		 * @protected
		 */
		protected var surface:Surface;
		
		/**
		 * @protected
		 */
		protected var gr:GeometryGroup;
		
		/**
		 * @protected
		 */
		protected var trunkStroke:LinearGradientStroke;
		
		//--------------------------------------
		//  Methods
		//--------------------------------------
		
		/**
		 * Constructor
		 */
		public function CarbonFunTree()
		{
			super();
			visible=false;
			
			horizontalScrollPolicy="off";
			verticalScrollPolicy="off";
		}
		
		/**
		 * @protected
		 */
		override protected function createChildren() : void{
			super.createChildren();
			
			surface=new Surface();
			gr=new GeometryGroup();
			
			surface.addChild(gr);
			rawChildren.addChild(surface);			
		}
		
		/**
		 * @protected
		 */
		override protected function commitProperties() : void{
			super.commitProperties();
			
			wipeDown=new WipeDown();
			wipeDown.duration=transitionTime*1000;
			
			wipeUp=new WipeUp();
			wipeUp.duration=transitionTime*1000;	
			
			if(!styleName)
				styleName="carbonFunTree";			
		}
		
		/**
		 * @protected
		 */
		protected function generateTree(aWidth:Number,aHeight:Number):void{	
			surface.removeChild(gr);
			gr=new GeometryGroup();
			surface.addChild(gr);
			
			trunkStroke=new LinearGradientStroke();
			
			if(getStyle("trunkWeight"))
				trunkStroke.weight=getStyle("trunkWeight");
			else
				trunkStroke.weight=StyleManager.getStyleDeclaration(".carbonFunTree").getStyle("trunkWeight");
			
			trunkStroke.gradientStops=GradientStopFactory.createGradientStops(this, "trunk");
			
			_positions=new Array();
					
			var startX:Number=aWidth/2;
			var startY:Number=aHeight;
			
			var lastY:Number=startY;
			
			var stepY:Number=aHeight/numChildren;
			
			var spline:BezierSpline=new BezierSpline(new Array());			
			spline.stroke=trunkStroke;			
			
			spline.addControlPoint(startX,startY);
			
			for(var i:uint=0;i<numChildren+1;i++){
				var growY:Number=stepY;
				
				var knotX:Number=startX + maxKnotSpreadX * MathUtils.randRange(-1, 1);
				var knotY:Number;
				if(direction==UP_DIRECTION)
					knotY=lastY - growY;
				else if(direction==DOWN_DIRECTION)
					knotY=lastY + growY;
				
				lastY=knotY;
				
				spline.addControlPoint(knotX,knotY);
				
				var branchDirection:Number= i%2==0? 1: -1;
				
				if(i!=numChildren)
					generateBranch(knotX,knotY,branchDirection);
				else
					generateBranch(knotX,knotY,branchDirection,false);
			}

			gr.geometryCollection.addItem(spline);			
		}

		/**
		 * @protected
		 */
		protected function generateBranch(startX:Number,startY:Number,branchDirection:Number,drawable:Boolean=true):void{
			var branchSteps:Number=Math.floor(MathUtils.randRange(minBranchSteps,maxBranchSteps));
			var lastX:Number=startX;
			var lastY:Number=startY;
			
			var widgetX:Number;
			var widgetY:Number;
			
			var branch:BezierSpline=new BezierSpline(new Array());
			branch.stroke=trunkStroke;
			branch.addControlPoint(startX,startY);
			
			for(var i:uint=1;i<=branchSteps;i++){
				var branchSpreadX:Number=maxBranchSpreadX/branchSteps * Math.random();
				var branchSpreadY:Number=maxBranchSpreadY * MathUtils.randRange(1,1);
				
				var knotX:Number= branchDirection>0? lastX+branchSpreadX : lastX-branchSpreadX;
				var knotY:Number=lastY+branchSpreadY;	
				
				lastX=knotX;
				lastY=knotY;
				
				if(i==branchSteps-1){
					widgetX=lastX;
					widgetY=lastY;
				}
				
				if(drawable)
					branch.addControlPoint(knotX,knotY);
			}
			
			_positions.push({x:widgetX,y:widgetY,branchDirection:branchDirection});
			
			gr.geometryCollection.addItem(branch);
			
		}
		
		/**
		 * @public
		 */		
		public function popUp():void{
			if(_host==null)
				_host=parent;
						
			var hostCenter:Point=new Point(_host.x,_host.y);
			hostCenter=_host.parent.localToGlobal(hostCenter);
			
			x=hostCenter.x+ (_host.width-this.width)/2;
			y=hostCenter.y-this.height;
			
			generateTree(width,height);
			
			if(parent)
				parent.removeChild(this);
			
			visible=true;
			
			PopUpManager.addPopUp(this,_host,false);
			
			for(var i:uint=0;i<_positions.length-1;i++){
				var child:UIComponent=getChildAt(i) as UIComponent;
				
				var pt:Object=_positions[i];
				
				if(pt.branchDirection>0)
					child.x=pt.x;
				else
					child.x=pt.x-child.measuredWidth;
				child.y=pt.y-child.measuredHeight/2;
			}
			
			if(direction==UP_DIRECTION)
				wipeUp.play([this]);
			else if(direction==DOWN_DIRECTION)
				wipeDown.play([this]);
				
			addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE,close);	
		}
		
		/**
		 * @public
		 */		
		public function close(evt:Event=null):void{
			PopUpManager.removePopUp(this);

			removeEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE,close);	
			
			_positions=[];
			visible=false;
		}
						
		/**
		 * @public
		 */
		public function get defaultStyleName():String{
			return "carbonFunTree";
		}
	}
}